package cn.template.serve_project.config.shiro.filter;

import cn.template.serve_project.config.shiro.JwtToken;
import cn.template.serve_project.constant.HeaderConstant;
import cn.template.serve_project.constant.JwtConstant;
import cn.template.serve_project.enums.R;
import cn.template.serve_project.result.Result;
import cn.template.serve_project.util.HttpUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.context.MessageSource;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Locale;
import java.util.Objects;

/*
 * 自定义一个Filter，用来拦截所有的请求判断是否携带Token
 * isAccessAllowed()判断是否携带了有效的JwtToken
 * onAccessDenied()是没有携带JwtToken的时候进行账号密码登录，登录成功允许访问，登录失败拒绝访问
 * */
@Slf4j
public class JwtFilter extends AccessControlFilter {

    @Resource
    MessageSource messageSource;

    /*
     * 1. 返回true，shiro就直接允许访问url
     * 2. 返回false，shiro才会根据onAccessDenied的方法的返回值决定是否允许访问url
     * */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        //这里让它始终返回false来使用onAccessDenied()方法
        return false;
    }

    /**
     * 返回结果为true表明登录通过
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader(JwtConstant.TOKEN);
        JwtToken jwtToken = new JwtToken(jwt);
        try {
            // 委托 realm 进行登录认证
            //所以这个地方最终还是调用JwtRealm进行的认证
            getSubject(servletRequest, servletResponse).login(jwtToken);
            //也就是subject.login(token)
        } catch (Exception e) {
            onLoginFail(servletResponse);
            //调用下面的方法向客户端返回错误信息
            return false;
        }

        return true;
        //执行方法中没有抛出异常就表示登录成功
    }

    //登录失败时默认返回 401 状态码
    private void onLoginFail(ServletResponse response) throws IOException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        Result<?> error = Result.error(R.ERROR_ATTESTATION_NO.code, handlerMsg(R.ERROR_ATTESTATION_NO.msg));
        httpResponse.getWriter().write(JSON.toJSONString(error));
    }

    public static String getLocal() {
        String lang = HttpUtil.getHttpInfo().getHeader(HeaderConstant.LANGUAGE);
        if (Objects.nonNull(lang)) {
            return lang;
        }
        return "zh";
    }

    public String handlerMsg(String msg) {
        try {
            return messageSource.getMessage(msg, null, Locale.forLanguageTag(getLocal()));
        } catch (Exception e) {
            return msg;
        }
    }
}
